﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using eBook.Core.BusinessObjects;
using eBook.Core.Contracts;
using eBook.Core;
using System.Threading;
using System.Reflection;

namespace eBook.Services
{
  public enum BookInfoProvider
  {
    Amazon,
    LibraryOfCongress,
    OpenLibrary,
    IsbnDb
  }

  public class AWSLCBookMashup : IBookMashup
  {
    private delegate boBookInfo GetBookInfoDelegate(ISBN isbn);
    private Dictionary<GetBookInfoDelegate, bool> _completed = new Dictionary<GetBookInfoDelegate, bool>();
    private List<boBookInfo> _searchResult = new List<boBookInfo>();

    public bool IsBusy 
    {
      get { return _completed.Values.Contains(false); }
    }

    public List<boBookInfo> GetBookInfoByISBN(ISBN isbn)
    {
      _searchResult.Clear();
      this.GetBookInfoByISBN(isbn, BookInfoProvider.Amazon);
      this.GetBookInfoByISBN(isbn, BookInfoProvider.LibraryOfCongress);
      this.GetBookInfoByISBN(isbn, BookInfoProvider.OpenLibrary);

      while (this.IsBusy)
        Thread.Sleep(100);

      _completed.Clear();

      return _searchResult;
    }

    public boBookInfo GetMashedInfoByISBN(ISBN isbn)
    {
      List<boBookInfo> list = this.GetBookInfoByISBN(isbn);
      return this.MashUp(list);
    }

    private boBookInfo MashUp(List<boBookInfo> list)
    {
      if ( list == null || list.Count == 0)
        return new boBookInfo() { InfoProvider = "MASH"};

      if (list.Count == 1)
      {
        list[0].InfoProvider = "MASH";
        return list[0];
      }

      // favor aws result
      boBookInfo item = list.FirstOrDefault(x => x.InfoProvider == "AWS");
      if (item == null)
        item = list[0] != null ? list[0] : list[1];

      item.InfoProvider = "MASH";

      boBookInfo pivot = list.FirstOrDefault(x => x.InfoProvider != item.InfoProvider); ;
      if (pivot == null)
        return item;

      PropertyInfo[] props = typeof(boBookInfo).GetProperties();
      foreach (var p in props)
      {        
        object itemPropValue = p.GetValue(item, null);
        object pivotPropValue = p.GetValue(pivot, null);

        if (itemPropValue == null || itemPropValue == this.GetDefaultValueForType(p.PropertyType))
          p.SetValue(item, pivotPropValue, null);
        else
        {
          if (p.PropertyType.IsArray)
          {
            Array a1 = itemPropValue as Array;
            Array a2 = pivotPropValue as Array;
            
           
            int totalLength = a1.Length + a2.Length;
            Array a = Array.CreateInstance(p.PropertyType.GetElementType(), totalLength);
            
            a1.CopyTo(a, 0);
            a2.CopyTo(a, a1.Length);

            p.SetValue(item, a, null);            
          }
        }

      }

      
      return item;
    }

    private object GetDefaultValueForType(Type type)
    {
      if (type == typeof(string))
        return string.Empty;

      if (type.IsValueType)      
        return Activator.CreateInstance(type);
      
      return null;
    }



    private void GetBookInfoByISBN(ISBN isbn, BookInfoProvider provider)
    {
      IBookLookup lookup = null;
      switch (provider)
      {
        case BookInfoProvider.Amazon:
          lookup = new AmazonBookLookup();
          break;

        case BookInfoProvider.LibraryOfCongress:
          lookup = new LCBookLookup();
          break;

        case BookInfoProvider.OpenLibrary:
          lookup = new OpenLibraryBookLookup();
          break;

        case BookInfoProvider.IsbnDb:
          lookup = new IsbnDbBookLookup();
          break;

        default:
          break;
      }

      GetBookInfoDelegate getter = new GetBookInfoDelegate(lookup.GetBookInfoByISBN);
      _completed.Add(getter, false);
      getter.BeginInvoke(isbn, new AsyncCallback(OnLookupComplete), getter);
    }

    private void OnLookupComplete(IAsyncResult state)
    {
      GetBookInfoDelegate getter = (GetBookInfoDelegate) state.AsyncState;      
      boBookInfo info = getter.EndInvoke(state);
      if (_completed.ContainsKey(getter))
      {
        _completed[getter] = true;
        _searchResult.Add(info);
      }
    }





  }
}
